/*
 * ListView.java
 *
 * Created on April 24, 2002, 10:05 AM
 */

package fr.umlv.cmis.listview;

import java.applet.Applet;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JList;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;

import fr.umlv.dnd.CustomDropTargetListener;


// TODO: Auto-generated Javadoc
/**
 * The Class ListView.
 */
public class ListView extends JList implements ViewComponent {
   
	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1L;

	/** The Constant ICON_SIZE. */
	private static final int ICON_SIZE = 24;
	
	/** The editor remover. */
    private PropertyChangeListener      editorRemover = null;
    
    /** The editor comp. */
    private Component       editorComp;
    
    /** The cell editor. */
    protected ListCellEditor cellEditor;
    
    /** The editing index. */
    protected int editingIndex = -1;
    
    /** The data model. */
    protected ListModel dataModel;
    
    /**
     * Instantiates a new list view.
     *
     * @param dataModel the data model
     */
    public ListView(CmisObjectListViewModel dataModel) {
        super(dataModel);
        this.dataModel = dataModel;
        ListCellRenderer cellRenderer = new ListCellRenderer(this);
        
        cellEditor = new ListCellEditor();
        cellEditor.addCellEditorListener(new javax.swing.event.CellEditorListener() {
            public void editingCanceled(ChangeEvent evt) { ListView.this.editingCanceled(evt); }
            public void editingStopped(ChangeEvent evt) { ListView.this.editingStopped(evt); }
        });
        
        addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                onListSelectionChanged(e);
            }
        });
        
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(ComponentEvent evt) { onComponentResized(evt); }
        });
        
        //setTransferHandler(new MyTransferHandler());
        new CustomDropTargetListener(this);
        setFont(UIManager.getFont("Table.font"));
        setLayoutOrientation(JList.VERTICAL_WRAP);
        setCellRenderer(cellRenderer);
    }
    
    /* (non-Javadoc)
     * @see fr.umlv.cmis.listview.ViewComponent#getViewModel()
     */
    public CmisObjectListViewModel getViewModel() {
        return (CmisObjectListViewModel)getModel();
    }
    
    /**
     * Sets the editing index.
     *
     * @param index the new editing index
     */
    private void setEditingIndex(int index) {
        editingIndex = index;
    }
    
    /**
     * Gets the editing index.
     *
     * @return the editing index
     */
    public int getEditingIndex() {
        return editingIndex;
    }
    
    /**
     * Gets the cell editor.
     *
     * @return the cell editor
     */
    public ListCellEditor getCellEditor() {
        return cellEditor;
    }
    
    /**
     * Removes the editor.
     */
    private void removeEditor() {
        KeyboardFocusManager.getCurrentKeyboardFocusManager().
        removePropertyChangeListener("permanentFocusOwner", editorRemover);
        editorRemover = null;
        
        ListCellEditor editor = getCellEditor();
        if(editor != null) {
            //editor.removeCellEditorListener(this);
            
            if (editorComp != null) {
                remove(editorComp);
            }
            
            int index = getEditingIndex();
            Rectangle cellRect = getCellBounds(index,index);
            
            setEditingIndex(-1);
            editorComp = null;
            
            if (cellRect != null) { repaint(cellRect); }
        }
    }
    
    
    /**
     * Checks if is editing.
     *
     * @return true, if is editing
     */
    public boolean isEditing() {
        boolean b = false;
        CmisObjectListViewModel dataModel = (CmisObjectListViewModel)getModel();
        if (dataModel.getState() == CmisObjectListViewModel.EDIT) { b = true; }
        return b;
        
    }
    
    /**
     * Edits the cell at.
     *
     * @param index the index
     * @return true, if successful
     */
    public boolean editCellAt(int index) {
        boolean b = false;
        
        if (editorRemover == null) {
            KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
            editorRemover = new CellEditorRemover(fm);
            fm.addPropertyChangeListener("permanentFocusOwner", editorRemover);
        }
        
        CmisObjectListViewModel dataModel = (CmisObjectListViewModel)getModel();
        
        if ( (index < 0) || (index >= dataModel.getRowCount()) ) {
            throw new IndexOutOfBoundsException("invalid index");
        }
        
        ListSelectionModel selectionModel = getSelectionModel();
        selectionModel.clearSelection();
        selectionModel.setSelectionInterval(index,index);
        setEditingIndex(index);
        
        Object value = dataModel.getFile(index);
        editorComp = cellEditor.getListCellEditorComponent(this, value, true, index);
        editorComp.setBounds(getCellBounds(index,index));
        add(editorComp);
        editorComp.validate();
        editorComp.repaint();
        editorComp.requestFocusInWindow();
        
        b = true;
        return b;
    }
    
    /**
     * Editing canceled.
     *
     * @param evt the evt
     */
    public void editingCanceled(ChangeEvent evt) {
    	CmisObjectListViewModel dataModel = (CmisObjectListViewModel)getModel();
        dataModel.setState(CmisObjectListViewModel.BROWSE);
        removeEditor();
    }
    
    /**
     * Editing stopped.
     *
     * @param evt the evt
     */
    public void editingStopped(ChangeEvent evt) {
    	CmisObjectListViewModel dataModel = (CmisObjectListViewModel)getModel();
        dataModel.setState(CmisObjectListViewModel.BROWSE);
        removeEditor();
    }
    
    //////////////////////////
    // event handling
    /////////////////////////
    
    /**
     * On component resized.
     *
     * @param evt the evt
     */
    private void onComponentResized(ComponentEvent evt) {
        if (getModel().getSize() <= 0) {
            return;
        }
        
        int rowCount = 8; // 8 as default
        Rectangle rect  = getCellBounds(0,0);
        Rectangle size = getVisibleRect();
        double d = size.getHeight()/rect.getHeight();
        if (d > rowCount) {
            rowCount = (int)d;
            rowCount = rowCount - 1;
        }
        setVisibleRowCount(rowCount);
    }
    
    /**
     * On list selection changed.
     *
     * @param e the e
     */
    private void onListSelectionChanged(ListSelectionEvent e) {
        if (e.getValueIsAdjusting()) { return; }
        getCellEditor().cancelCellEditing();
    }
    
    // This class tracks changes in the keyboard focus state. It is used
    // when the ListView is editing to determine when to cancel the edit.
    // If focus switches to a component outside of the ListView, but in the
    // same window, this will cancel editing.
    /**
     * The Class CellEditorRemover.
     */
    public class CellEditorRemover implements PropertyChangeListener {
        
        /** The focus manager. */
        KeyboardFocusManager focusManager;
        
        /**
         * Instantiates a new cell editor remover.
         *
         * @param fm the fm
         */
        public CellEditorRemover(KeyboardFocusManager fm) {
            this.focusManager = fm;
        }
        
        /* (non-Javadoc)
         * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
         */
        public void propertyChange(PropertyChangeEvent evt) {
            if (!isEditing() || getClientProperty("terminateEditOnFocusLost") != Boolean.TRUE) {
                return;
            }
            
            Component c = focusManager.getPermanentFocusOwner();
            while (c != null) {
                if (c == ListView.this) {
                    // focus remains inside the list
                    return;
                } else if ((c instanceof Window) || (c instanceof Applet && c.getParent() == null)) {
                    if (c == SwingUtilities.getRoot(ListView.this)) {
                        if (!getCellEditor().stopCellEditing()) {
                            getCellEditor().cancelCellEditing();
                        }
                    }
                    break;
                }
                c = c.getParent();
            }
        }
    }
    
    /**
     * Gets the data model.
     *
     * @return the data model
     */
    public ListModel getDataModel() {
		return dataModel;
	}

	/* (non-Javadoc)
	 * @see fr.umlv.cmis.listview.ViewComponent#getIconSize()
	 */
	@Override
	public int getIconSize() {
		return ICON_SIZE;
	}
}
